<template>
  <!-- 初始化表格的宽度和高度 -->
	<view
		class="nhu-table"
		:style="{
			width: width,
			height: height
		}"
	>
		<scroll-view
			id="table"
			scroll-x
			:scroll-y="height ? true : false"
			class="box"
			:style="{
				width: width,
				height: height
			}"
			:scroll-left="scrollLeftMove"
			:scroll-top="scrollTopMove"
			@scroll="scroll"
			@scrolltoupper="scrolltoupper"
			@scrolltolower="scrolltolower"
		>
			<view
				id="table-body"
				class="table"
				:style="{
					width: tableWidth + 'rpx'
				}"
			>
				<view id="table-th" class="th" :style="{ height: thHeight }">
					<view
						v-for="(td, index) in th"
						:key="index"
						:class="{ td, 'fixed-left': td.fixed && index == 0, 'fixed-right': td.fixed && index == th.length - 1 }"
						:style="{
							width: td.width || tdWidth,
							'box-shadow': td.fixed ? (index == 0 ? '1px 0 4px -4px #999' : index == th.length - 1 ? '-1px 0 4px -4px #999' : 'none') : 'none'
						}"
						@click="onClick('th', index)"
					>
						<view
							:class="['td-text', textLine === 1 ? 'td-text-line' : 'td-text-line2']"
							:style="{
								'text-align': (td.thStyle && td.thStyle.align) || 'left',
								color: (td.thStyle && td.thStyle.color) || '#3d424d'
							}"
						>
							{{ td.title }}
						</view>
					</view>
				</view>
				<view v-if="showTotal" class="tr" :style="{ height: totalHeight }">
					<view
						v-for="(td, index) in th"
						:key="index"
						:class="{ td, 'fixed-left': td.fixed && index == 0, 'fixed-right': td.fixed && index == th.length - 1 }"
						:style="{
							width: td.width || tdWidth,
							'box-shadow': td.fixed ? (index == 0 ? '1px 0 4px -4px #999' : index == th.length - 1 ? '-1px 0 4px -4px #999' : 'none') : 'none'
						}"
						@click="onClick('total', index)"
					>
						<view
							:class="['td-text', textLine === 1 ? 'td-text-line' : 'td-text-line2']"
							:style="{
								color: colorFn(totalData[index], td),
								'text-align': alignFn(totalData[index], td)
							}"
						>
							{{ totalData[index] | dataFormat(td) }}
						</view>
					</view>
				</view>
				<view class="tr" v-for="(data, k) in listData" :key="k" :style="{ height: trHeight }">
					<view
						v-for="(td, index) in th"
						:key="index"
						:class="{ td, 'fixed-left': td.fixed && index == 0, 'fixed-right': td.fixed && index == th.length - 1 }"
						:style="{
							width: td.width || tdWidth,
							'box-shadow': td.fixed ? (index == 0 ? '1px 0 4px -4px #999' : index == th.length - 1 ? '-1px 0 4px -4px #999' : 'none') : 'none'
						}"
						@click="onClick(k, index)"
					>
						<view
							:class="['td-text', textLine === 1 ? 'td-text-line' : 'td-text-line2']"
							:style="{
								color: colorFn(data[td.key], td),
								'text-align': alignFn(data[td.key], td)
							}"
						>
							{{ data[td.key] | dataFormat(td) }}
						</view>
					</view>
				</view>
			</view>
		</scroll-view>
		<view
			v-if="info.with < info.bodyWidth"
			class="width-scroll-bar"
			:style="{
				backgroundColor: scrollBarBgColor
			}"
		>
			<view
				class="width-scroll-bar-active"
				:style="{
					width: (info.with / info.bodyWidth) * 100 + '%',
					transform: 'translateX(' + barMarginLeft + ')',
					backgroundColor: scrollBarColor
				}"
				@touchstart="widthScrollStart"
				@touchmove="widthScrollMove"
				@touchend="widthScrollEnd"
			></view>
		</view>
		<view
			v-if="info.height < info.bodyHeight"
			class="height-scroll-bar"
			:style="{
				backgroundColor: scrollBarBgColor
			}"
		>
			<view
				class="height-scroll-bar-active"
				:style="{
					height: (info.height / info.bodyHeight) * 100 + '%',
					transform: 'translateY(' + barMarginTop + ')',
					backgroundColor: scrollBarColor
				}"
				@touchstart="heightScrollStart"
				@touchmove="heightScrollMove"
				@touchend="heightScrollEnd"
			></view>
		</view>
	</view>
</template>

<script>
export default {
	name: 'nhu-table',
	props: {
		listData: {
			type: Array,
			default: function() {
				return [];
			}
		},
		initWidth: {
			type: String,
			default: '686rpx'
		},
		initHeight: {
			type: String,
			default: ''
		},
		initThHeight: {
			type: String,
			default: '104rpx'
		},
		initTrHeight: {
			type: String,
			default: '120rpx'
		},
		initTotalHeight: {
			type: String,
			default: '96rpx'
		},
		initTextLine: {
			type: Number,
			default: 2
		},
		initTh: {
			type: Array,
			default: function() {
				return [];
			}
		},
		initTotalRule: {
			type: Array,
			default: function() {
				return [];
			}
		},
		initScrollBarBgColor: {
			type: String,
			default: '#f3f3f3'
		},
		initScrollBarColor: {
			type: String,
			default: '#ccc'
		}
	},
	data() {
		return {
			timer: '',
			rpxTopx: 2,
			tdWidth: '180rpx',
			width: '686rpx',
			height: '',
			thHeight: '104rpx',
			trHeight: '120rpx',
			totalHeight: '96rpx',
			textLine: 2,
			totalRule: [],
			th: [],
			scrollLeft: 0,
			scrollTop: 0,
			scrollLeftMove: 0,
			scrollTopMove: 0,
			scrollBarBgColor: '#f3f3f3',
			scrollBarColor: '#ccc',
			info: {
				with: 0,
				height: 0,
				bodyWidth: 0,
				bodyHeight: 0
			},
			bar: {
				x: 0,
				y: 0
			}
		};
	},
	watch: {
		listData(d) {
			this.$nextTick(() => {
				this.getTableInfo();
			});
			return d;
		},
		initWidth(d) {
			this.init({
				width: d
			});
		},
		initHeight(d) {
			this.init({
				height: d
			});
		},
		initThHeight(d) {
			this.init({
				thHeight: d
			});
		},
		initTrHeight(d) {
			this.init({
				trHeight: d
			});
		},
		initTotalHeight(d) {
			this.init({
				totalHeight: d
			});
		},
		initTextLine(d) {
			this.init({
				textLine: d
			});
		},
		initTh(d) {
			this.init({
				th: d
			});
		},
		initTotalRule(d) {
			this.init({
				totalRule: d
			});
		},
		initScrollBarBgColor(d) {
			this.init({
				scrollBarBgColor: d
			});
		},
		initScrollBarColor(d) {
			this.init({
				scrollBarColor: d
			});
		}
	},
	computed: {
		barMarginLeft() {
			return (this.scrollLeft / this.info.bodyWidth) * this.info.with + 'px';
		},
		barMarginTop() {
			return (this.scrollTop / this.info.bodyHeight) * this.info.height + 'px';
		},
		tableWidth() {
			let tableWidth = 0;
			this.th.map(td => {
				tableWidth += parseInt(td.width) || parseInt(this.tdWidth);
			});
			return tableWidth;
		},
		showTotal() {
			return this.totalRule.length > 0 ? true : false;
		},
		totalData() {
			let data = [],
				total = [],
				keys = [];
			if (this.totalRule.length > 0 && this.th.length > 0) {
				this.th.map(td => {
					data.push([]);
					total.push('');
					keys.push(td.key);
				});
				this.listData.map(list => {
					keys.map((key, index) => {
						data[index].push(list[key]);
					});
				});
				this.totalRule.map((rule, index) => {
					if (typeof rule === 'function') {
						total[index] = rule(data[index], this.listData);
					} else {
						if (rule == 'average' || rule == 'add') {
							total[index] = getTotal(rule, data[index]);
						} else {
							total[index] = rule;
						}
					}
					if (!total[index] && (total[index] === null || total[index] === undefined || isNaN(total[index]))) total[index] = '';
				});
				function getTotal(type, data) {
					let all = 0;
					data.map(d => {
						all += parseFloat(d) || 0;
					});
					return Number(parseFloat(all / (type == 'average' ? data.length : 1)).toFixed(2));
				}
			}
			return total;
		}
	},
	filters: {
		dataFormat(data, td) {
			return td.dataValues ? td.dataValues[data] || '' : td.dataFormat ? td.dataFormat(data) : data === undefined || data == null ? '' : data;
		}
	},
	mounted() {
		this.rpxTopx = 750 / uni.getSystemInfoSync().windowWidth;
		this.init({
			width: this.initWidth,
			height: this.initHeight,
			thHeight: this.initThHeight,
			trHeight: this.initTrHeight,
			textLine: this.initTextLine,
			th: this.initTh,
			totalRule: this.initTotalRule,
			scrollBarBgColor: this.initScrollBarBgColor,
			scrollBarColor: this.initScrollBarColor
		});
		this.$parent.$refs[this.$scope.dataset.ref] = this;
		this.$emit('onLoad');
		this.getTableInfo();
	},
	destroyed() {
		delete this.$parent.$refs[this.$scope.dataset.ref];
	},
	methods: {
		init(parm) {
			if (parm.width !== undefined) this.width = parm.width;
			if (parm.height !== undefined) this.height = parm.height;
			if (parm.thHeight !== undefined) this.thHeight = parm.thHeight;
			if (parm.trHeight !== undefined) this.trHeight = parm.trHeight;
			if (parm.totalHeight !== undefined) this.totalHeight = parm.totalHeight;
			if (parm.textLine !== undefined) this.textLine = parm.textLine;
			if (parm.th !== undefined) this.th = parm.th;
			if (parm.totalRule !== undefined) this.totalRule = parm.totalRule;
			if (parm.scrollBarBgColor !== undefined) this.scrollBarBgColor = parm.scrollBarBgColor;
			if (parm.scrollBarColor !== undefined) this.scrollBarColor = parm.scrollBarColor;
			this.getTableInfo();
		},
		colorFn(data, td) {
			return (td.dataStyle && td.dataStyle.color && (typeof td.dataStyle.color == 'function' ? td.dataStyle.color(data) : td.dataStyle.color)) || '#14161a';
		},
		alignFn(data, td) {
			return (td.dataStyle && td.dataStyle.align && (typeof td.dataStyle.align == 'function' ? td.dataStyle.align(data) : td.dataStyle.align)) || 'left';
		},
		onClick(type, index) {
			let data = {
				type: typeof type === 'number' ? 'listData' : type,
				index: typeof type === 'number' ? type : -1,
				key: this.th[index].key,
				data: typeof type === 'number' ? this.listData[type] : type === 'total' ? this.totalData : this.th
			};
			if (typeof type === 'number' && this.th[index].dataClick) {
				this.th[index].dataClick(data.data, type);
			}
			this.$emit('tdClick', data);
		},
		getTableInfo() {
			clearTimeout(this.timer);
			this.timer = setTimeout(d => {
				const query = uni.createSelectorQuery().in(this);
				query
					.select('#table')
					.boundingClientRect(data => {
						this.info.with = data.width;
						this.info.height = data.height;
					})
					.select('#table-body')
					.boundingClientRect(data => {
						this.info.bodyWidth = data.width;
						this.info.bodyHeight = data.height;
					})
					.exec();
			}, 100);
		},
		scroll(e) {
			if (this.bar.x == 0 && this.bar.y == 0) {
				this.scrollLeft = e.detail.scrollLeft;
				this.scrollTop = e.detail.scrollTop;
				this.$emit('scroll', e);
			}
		},
		scrolltoupper(e) {
			if (e.detail.direction == 'top') {
				this.$emit('scrollTop');
			}
			if (e.detail.direction == 'left') {
				this.$emit('scrollLeft');
			}
		},
		scrolltolower(e) {
			if (e.detail.direction == 'bottom') {
				this.$emit('scrollBottom');
			}
			if (e.detail.direction == 'right') {
				this.$emit('scrollRight');
			}
		},
		widthScrollStart(e) {
			let touch = e.changedTouches[0];
			if (touch.pageX == 0 && touch.pageY == 0) return;
			this.bar.x = touch.pageX;
		},
		widthScrollMove(e) {
			let touch = e.changedTouches[0];
			if (touch.pageX == 0 && touch.pageY == 0) return;
			let cont = touch.pageX - this.bar.x;
			this.bar.x = touch.pageX;
			if (Math.abs(cont) > 50) return;
			let scrollLeft = this.scrollLeft + (cont / this.info.with) * this.info.bodyWidth;
			this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft > this.info.bodyWidth - this.info.with ? this.info.bodyWidth - this.info.with : scrollLeft;
			this.scrollLeftMove = this.scrollLeft;
		},
		widthScrollEnd() {
			this.bar.x = 0;
		},
		heightScrollStart(e) {
			let touch = e.changedTouches[0];
			if (touch.pageX == 0 && touch.pageY == 0) return;
			this.bar.y = touch.pageY;
		},
		heightScrollMove(e) {
			let touch = e.changedTouches[0];
			if (touch.pageX == 0 && touch.pageY == 0) return;
			let cont = touch.pageY - this.bar.y;
			this.bar.y = touch.pageY;
			if (Math.abs(cont) > 50) return;
			let scrollTop = this.scrollTop + (cont / this.info.height) * this.info.bodyHeight;
			this.scrollTop = scrollTop < 0 ? 0 : scrollTop > this.info.bodyHeight - this.info.height ? this.info.bodyHeight - this.info.height : scrollTop;
			this.scrollTopMove = this.scrollTop;
		},
		heightScrollEnd() {
			this.bar.y = 0;
		}
	}
};
</script>

<style scoped>
.fixed-left {
	position: sticky;
	left: 0;
	z-index: 2;
}
.fixed-right {
	position: sticky;
	right: 0;
	z-index: 2;
}
.nhu-table {
	position: relative;
}
.table .th,
.table .tr {
	display: flex;
	align-items: stretch;
	justify-content: flex-start;
	height: 96rpx;
}
.table .th {
	position: sticky;
	top: 0;
	border-radius: 8rpx;
	z-index: 3;
}
.table .th .td {
	background: #f2f5fa;
}
.table .tr:nth-child(2n + 1) .td {
	background: #fafbfc;
}
.table .th .td-text {
	color: #3d424d;
	font-weight: bold;
}
.table .td {
	padding: 0 24rpx;
	flex-shrink: 0;
	flex-grow: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: #fff;
	box-sizing: border-box;
}
.td-text {
	width: 100%;
	font-size: 28rpx;
	color: #14161a;
	line-height: 36rpx;
	text-align: left;
	overflow: hidden;
	text-overflow: ellipsis;
}
.td-text-line {
	white-space: nowrap;
}
.td-text-line2 {
	word-break: break-all;
	text-overflow: -o-ellipsis-lastline;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}
.width-scroll-bar {
	position: absolute;
	left: 0;
	right: 0;
	bottom: -4rpx;
	width: 100%;
	height: 8rpx;
	border-radius: 8rpx;
	z-index: 9;
}
.width-scroll-bar-active {
	height: 8rpx;
	border-radius: 8rpx;
	margin: 0;
	transition: all ease 100ms;
}
.width-scroll-bar-active:hover,
.width-scroll-bar-active:active {
	margin-top: -8rpx;
	height: 16rpx;
	border-radius: 16rpx;
}
.height-scroll-bar {
	position: absolute;
	top: 0;
	right: -4rpx;
	bottom: 0;
	width: 8rpx;
	height: 100%;
	border-radius: 8rpx;
	z-index: 9;
}
.height-scroll-bar-active {
	width: 8rpx;
	border-radius: 8rpx;
	margin: 0;
	transition: all ease 100ms;
}
.height-scroll-bar-active:hover,
.height-scroll-bar-active:active {
	margin-left: -8rpx;
	width: 16rpx;
	border-radius: 16rpx;
}
</style>
